
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="./../../cesium/Cesium1.98/Widgets/widgets.css">
    <script type="text/javascript" src="./../../cesium/Cesium1.98/Cesium.js"></script>
</head>

<body style="margin: 0; overflow: hidden; background: #fff; width: 100%; height: 100%; position: absolute; top: 0">
    <div id="map" style="margin: 0 auto; width: 100%; height: 100%"></div>
    <script type="text/javascript">

        Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3ZjQ5ZGUzNC1jNWYwLTQ1ZTMtYmNjYS05YTY4ZTVmN2I2MDkiLCJpZCI6MTE3MTM4LCJpYXQiOjE2NzY0NDUyODB9.ZaNSBIfc1sGLhQd_xqhiSsc0yr8oS0wt1hAo9gbke6M'
        const viewer = new Cesium.Viewer('map', {});

        // 开启帧率
        viewer.scene.debugShowFramesPerSecond = true;
        // 深度监测
        viewer.scene.globe.depthTestAgainstTerrain = true;
        // 清除默认地形
        viewer.scene.terrainProvider = new Cesium.EllipsoidTerrainProvider({});

        // 加载火星地形
        viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
            url: 'http://data.mars3d.cn/terrain'
        })  

        const fs = `
uniform sampler2D colorTexture;  // 颜色纹理
uniform sampler2D depthTexture;  // 深度纹理
in vec2 v_textureCoordinates;  // 纹理坐标
uniform float u_earthRadiusOnCamera;
uniform float u_cameraHeight;
uniform float u_fogHeight;
uniform vec3 u_fogColor;
uniform float u_globalDensity;
// 通过深度纹理与纹理坐标得到世界坐标
vec4 getWorldCoordinate(sampler2D depthTexture, vec2 texCoords) {
	float depthOrLogDepth = czm_unpackDepth(texture(depthTexture, texCoords));
	vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depthOrLogDepth);
	eyeCoordinate = eyeCoordinate / eyeCoordinate.w;
	vec4 worldCoordinate = czm_inverseView * eyeCoordinate;
	worldCoordinate = worldCoordinate / worldCoordinate.w;
	return worldCoordinate;
}
// 计算粗略的高程，依赖js传递的相机位置处的地球高程u_earthRadiusOnCamera。好处是计算量非常低
float getRoughHeight(vec4 worldCoordinate) {
	float disToCenter = length(vec3(worldCoordinate));
	return disToCenter - u_earthRadiusOnCamera;
}
// 得到a向量在b向量的投影长度，如果同向结果为正，异向结果为复
float projectVector(vec3 a, vec3 b) {
	float scale = dot(a, b) / dot(b, b);
	float k = scale / abs(scale);
	return k * length(scale * b);
}
// 线性浓度积分高度雾
float linearHeightFog(vec3 positionToCamera, float cameraHeight, float pixelHeight, float fogMaxHeight) {
	float globalDensity = u_globalDensity / 10.0;
	vec3 up = -1.0 * normalize(czm_viewerPositionWC);
	float vh = projectVector(normalize(positionToCamera), up);
 
	// 让相机沿着视线方向移动 雾气产生距离 的距离
	float s = step(100.0, length(positionToCamera));
	vec3 sub = mix(positionToCamera, normalize(positionToCamera) * 100.0, s);
	positionToCamera -= sub;
	cameraHeight = mix(pixelHeight, cameraHeight - 100.0 * vh, s);
 
	float b = mix(cameraHeight, fogMaxHeight, step(fogMaxHeight, cameraHeight));
	float a = mix(pixelHeight, fogMaxHeight, step(fogMaxHeight, pixelHeight));
 
	float fog = (b - a) - 0.5 * (pow(b, 2.0) - pow(a, 2.0)) / fogMaxHeight;
	fog = globalDensity * fog / vh;
 
	if(abs(vh) <= 0.01 && cameraHeight < fogMaxHeight) {
		float disToCamera = length(positionToCamera);
		fog = globalDensity * (1.0 - cameraHeight / fogMaxHeight) * disToCamera;
	}
 
	fog = mix(0.0, 1.0, fog / (fog + 1.0));
 
	return fog;
}
void main(void) {
	vec4 color = texture(colorTexture, v_textureCoordinates);
	vec4 positionWC = getWorldCoordinate(depthTexture, v_textureCoordinates);
	float pixelHeight = getRoughHeight(positionWC);
	vec3 positionToCamera = vec3(vec3(positionWC) - czm_viewerPositionWC);
	float fog = linearHeightFog(positionToCamera, u_cameraHeight, pixelHeight, u_fogHeight);
	out_FragColor = mix(color, vec4(u_fogColor, 1.0), fog);
}`


        const customPostProcessStage = new Cesium.PostProcessStage({
            fragmentShader: fs,
            uniforms: {
                u_earthRadiusOnCamera: () => Cesium.Cartesian3.magnitude(viewer.camera.positionWC) - viewer.camera.positionCartographic.height,
                u_cameraHeight: () => viewer.camera.positionCartographic.height,
                u_fogColor: () => new Cesium.Color(0.8, 0.82, 0.84),
                u_fogHeight: () => 1000,
                u_globalDensity: () => 0.6,
            }
        })
        viewer.scene.postProcessStages.add(customPostProcessStage)

        viewer.camera.setView({
            destination:new Cesium.Cartesian3(-1386705.7605894802,5226754.975571179,3375582.2076837276),
            orientation: {
                heading:3.968066845543675, // east, default value is 0.0 (north)
                pitch: -0.300780994602595,    // default value (looking down)
                roll: 0.00007913394522685024                           // default value
            }
        });
    </script>
</body>

</html>